Contexto¶
Um banco de varejo gostaria de contratá-lo para criar um modelo de inadimplência de crédito para o portfólio de cartões de crédito. O banco espera que o modelo identifique os consumidores que provavelmente não pagam seus pagamentos com cartão de crédito nos próximos 12 meses. Este modelo será usado para reduzir as perdas futuras do banco. O banco está disposto para fornecer algumas amostras de dados que eles podem extrair atualmente de seus sistemas
Detalhes Sobre o Dataset¶
O signifcado de cada coluna do DataSet está sendo explicado abaixo:
- 1 CARDHLDR Dummy variable, 1 if application for credit card accepted, 0 if not
- 2 DEFAULT 1 if defaulted 0 if not (observed when CARDHLDR=1, 10,499 observations)
- 3 AGE Age in years plus twelfths of a year
- 4 ACADMOS months living at current address
- 5 ADEPCNT 1 + number of dependents
- 6 MAJORDRG Number of major derogatory reports
- 7 MINORDRG Number of minor derogatory reports
- 8 OWNRENT 1 if owns their home, 0 if rent
- 9 INCOME Monthly income (divided by 10,000)
- 10 SELFEMPL 1 if self employed, 0 if not
- 11 INCPER Income divided by number of dependents
- 12 EXP_INC Ratio of monthly credit card expenditure to yearly income
- 13 SPENDING Average monthly credit card expenditure (for CARDHOLDER = 1)
- 14 LOGSPEND Log of spending
Dataset Do Kaggle: https://www.kaggle.com/datasets/surekharamireddy/credit-data¶
## bibliotecas iniciais
import pandas as pd
import numpy as np
## importando o dataset
bank = pd.read_csv('credit_data.csv')
print(f'O dataset contém {bank.shape[0]} linhas e {bank.shape[1]} colunas.')
bank.head()
O dataset contém 13444 linhas e 14 colunas.
| CARDHLDR | DEFAULT | AGE | ACADMOS | ADEPCNT | MAJORDRG | MINORDRG | OWNRENT | INCOME | SELFEMPL | INCPER | EXP_INC | SPENDING | LOGSPEND | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 27.250000 | 4 | 0 | 0 | 0 | 0 | 1200.000000 | 0 | 18000.0 | 0.000667 | ||
| 1 | 0 | 0 | 40.833332 | 111 | 3 | 0 | 0 | 1 | 4000.000000 | 0 | 13500.0 | 0.000222 | ||
| 2 | 1 | 0 | 37.666668 | 54 | 3 | 0 | 0 | 1 | 3666.666667 | 0 | 11300.0 | 0.033270 | 121.9896773 | 4.8039364 |
| 3 | 1 | 0 | 42.500000 | 60 | 3 | 0 | 0 | 1 | 2000.000000 | 0 | 17250.0 | 0.048427 | 96.8536213 | 4.5732008 |
| 4 | 1 | 0 | 21.333334 | 8 | 0 | 0 | 0 | 0 | 2916.666667 | 0 | 35000.0 | 0.016523 | 48.1916700 | 3.8751862 |
bank.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 13444 entries, 0 to 13443 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CARDHLDR 13444 non-null int64 1 DEFAULT 13444 non-null int64 2 AGE 13444 non-null float64 3 ACADMOS 13444 non-null int64 4 ADEPCNT 13444 non-null int64 5 MAJORDRG 13444 non-null int64 6 MINORDRG 13444 non-null int64 7 OWNRENT 13444 non-null int64 8 INCOME 13444 non-null float64 9 SELFEMPL 13444 non-null int64 10 INCPER 13444 non-null float64 11 EXP_INC 13444 non-null float64 12 SPENDING 13444 non-null object 13 LOGSPEND 13444 non-null object dtypes: float64(4), int64(8), object(2) memory usage: 1.4+ MB
bank.describe()
| CARDHLDR | DEFAULT | AGE | ACADMOS | ADEPCNT | MAJORDRG | MINORDRG | OWNRENT | INCOME | SELFEMPL | INCPER | EXP_INC | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 | 13444.000000 |
| mean | 0.780943 | 0.074085 | 33.471828 | 55.318878 | 1.017257 | 0.462809 | 0.290539 | 0.455965 | 2509.527819 | 0.057944 | 21719.680793 | 0.070974 |
| std | 0.413623 | 0.261919 | 10.226484 | 63.089729 | 1.279098 | 1.432724 | 0.767620 | 0.498076 | 1252.946716 | 0.233646 | 13591.209469 | 0.103922 |
| min | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 50.000000 | 0.000000 | 362.500000 | 0.000088 |
| 25% | 1.000000 | 0.000000 | 25.666666 | 12.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1666.666667 | 0.000000 | 12000.000000 | 0.002706 |
| 50% | 1.000000 | 0.000000 | 31.500000 | 30.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 2166.666667 | 0.000000 | 19000.000000 | 0.039286 |
| 75% | 1.000000 | 0.000000 | 39.333332 | 72.000000 | 2.000000 | 0.000000 | 0.000000 | 1.000000 | 2916.666667 | 0.000000 | 27658.666504 | 0.095655 |
| max | 1.000000 | 1.000000 | 88.666664 | 576.000000 | 9.000000 | 22.000000 | 11.000000 | 1.000000 | 8333.250000 | 1.000000 | 150000.000000 | 2.037728 |
## alterando nomes das colunas
bank.columns = ['CARTAO_CRED', 'INADIMPLENCIA', 'IDADE', 'TEMPO_RESIDENCIA', 'DEPENDENTES', 'DEPREC_MAJOR', 'DEPREC_MINOR',
'CASA_PROPRIA', 'RENDA_MENSAL', 'AUTONOMO', 'RENDA/DEPENDENTES', 'EXP_INC', 'DESPESA_MENSAL_CC', 'REGISTRO_GASTOS']
bank
| CARTAO_CRED | INADIMPLENCIA | IDADE | TEMPO_RESIDENCIA | DEPENDENTES | DEPREC_MAJOR | DEPREC_MINOR | CASA_PROPRIA | RENDA_MENSAL | AUTONOMO | RENDA/DEPENDENTES | EXP_INC | DESPESA_MENSAL_CC | REGISTRO_GASTOS | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 27.250000 | 4 | 0 | 0 | 0 | 0 | 1200.000000 | 0 | 18000.000000 | 0.000667 | ||
| 1 | 0 | 0 | 40.833332 | 111 | 3 | 0 | 0 | 1 | 4000.000000 | 0 | 13500.000000 | 0.000222 | ||
| 2 | 1 | 0 | 37.666668 | 54 | 3 | 0 | 0 | 1 | 3666.666667 | 0 | 11300.000000 | 0.033270 | 121.9896773 | 4.8039364 |
| 3 | 1 | 0 | 42.500000 | 60 | 3 | 0 | 0 | 1 | 2000.000000 | 0 | 17250.000000 | 0.048427 | 96.8536213 | 4.5732008 |
| 4 | 1 | 0 | 21.333334 | 8 | 0 | 0 | 0 | 0 | 2916.666667 | 0 | 35000.000000 | 0.016523 | 48.1916700 | 3.8751862 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 13439 | 1 | 0 | 41.750000 | 12 | 1 | 0 | 0 | 0 | 2083.333333 | 1 | 12500.000000 | 0.047914 | 99.8216681 | 4.6033853 |
| 13440 | 1 | 0 | 48.500000 | 66 | 1 | 0 | 1 | 1 | 2583.333333 | 0 | 15500.000000 | 0.020469 | 52.8791657 | 3.9680094 |
| 13441 | 1 | 0 | 48.250000 | 2 | 2 | 0 | 0 | 1 | 3083.333333 | 0 | 12333.333008 | 0.111619 | 344.1574903 | 5.8410994 |
| 13442 | 1 | 0 | 24.833334 | 38 | 0 | 0 | 0 | 1 | 1416.666667 | 0 | 17000.000000 | 0.013096 | 18.5525005 | 2.9206046 |
| 13443 | 0 | 0 | 26.833334 | 12 | 2 | 8 | 3 | 1 | 1496.000000 | 0 | 5984.000000 | 0.000668 |
13444 rows × 14 columns
01 - Análise Exploratória Do Dataset¶
# Convertendo as colunas para float
bank['DESPESA_MENSAL_CC'] = pd.to_numeric(bank['DESPESA_MENSAL_CC'], errors='coerce')
bank['REGISTRO_GASTOS'] = pd.to_numeric(bank['REGISTRO_GASTOS'], errors='coerce')
bank.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 13444 entries, 0 to 13443 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CARTAO_CRED 13444 non-null int64 1 INADIMPLENCIA 13444 non-null int64 2 IDADE 13444 non-null float64 3 TEMPO_RESIDENCIA 13444 non-null int64 4 DEPENDENTES 13444 non-null int64 5 DEPREC_MAJOR 13444 non-null int64 6 DEPREC_MINOR 13444 non-null int64 7 CASA_PROPRIA 13444 non-null int64 8 RENDA_MENSAL 13444 non-null float64 9 AUTONOMO 13444 non-null int64 10 RENDA/DEPENDENTES 13444 non-null float64 11 EXP_INC 13444 non-null float64 12 DESPESA_MENSAL_CC 10499 non-null float64 13 REGISTRO_GASTOS 10499 non-null float64 dtypes: float64(6), int64(8) memory usage: 1.4 MB
## verificando nulos
bank.isnull().sum()
CARTAO_CRED 0 INADIMPLENCIA 0 IDADE 0 TEMPO_RESIDENCIA 0 DEPENDENTES 0 DEPREC_MAJOR 0 DEPREC_MINOR 0 CASA_PROPRIA 0 RENDA_MENSAL 0 AUTONOMO 0 RENDA/DEPENDENTES 0 EXP_INC 0 DESPESA_MENSAL_CC 2945 REGISTRO_GASTOS 2945 dtype: int64
bank.head()
| CARTAO_CRED | INADIMPLENCIA | IDADE | TEMPO_RESIDENCIA | DEPENDENTES | DEPREC_MAJOR | DEPREC_MINOR | CASA_PROPRIA | RENDA_MENSAL | AUTONOMO | RENDA/DEPENDENTES | EXP_INC | DESPESA_MENSAL_CC | REGISTRO_GASTOS | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 27.250000 | 4 | 0 | 0 | 0 | 0 | 1200.000000 | 0 | 18000.0 | 0.000667 | NaN | NaN |
| 1 | 0 | 0 | 40.833332 | 111 | 3 | 0 | 0 | 1 | 4000.000000 | 0 | 13500.0 | 0.000222 | NaN | NaN |
| 2 | 1 | 0 | 37.666668 | 54 | 3 | 0 | 0 | 1 | 3666.666667 | 0 | 11300.0 | 0.033270 | 121.989677 | 4.803936 |
| 3 | 1 | 0 | 42.500000 | 60 | 3 | 0 | 0 | 1 | 2000.000000 | 0 | 17250.0 | 0.048427 | 96.853621 | 4.573201 |
| 4 | 1 | 0 | 21.333334 | 8 | 0 | 0 | 0 | 0 | 2916.666667 | 0 | 35000.0 | 0.016523 | 48.191670 | 3.875186 |
import plotly.express as px
fig = px.histogram(data_frame=bank, x='RENDA_MENSAL')
fig.show()
## boxplot
fig = px.box(data_frame=bank, y='IDADE')
fig.show()
## boxplot para renda mensal
fig = px.box(data_frame=bank, y='RENDA_MENSAL')
fig.show()
Podemos ver alguns outliers em termos de idade e renda mensal, isso pode trazer desequilíbrio nas análises
fig = px.density_heatmap(data_frame=bank, x='RENDA/DEPENDENTES')
fig.show()
fig = px.histogram(data_frame=bank, x='CARTAO_CRED', color='CARTAO_CRED', barmode='group')
fig.show()
fig = px.histogram(bank, x='CARTAO_CRED', color='INADIMPLENCIA',
title='Relação entre Possui Cartão e Inadimplência',
labels={'Possui_Cartao': 'Possui Cartão', 'Inadimplente': 'Inadimplente'},
barmode='group')
fig.update_layout(bargap=0.2) # Ajusta o espaço entre as barras agrupadas
fig.show()
02 - Breve Limpeza do Dataset¶
Ponto de atenção olhando as análises até então:
- Existem menores de idade com cartão - descartar da base?
- População SEM cartão não tem registro de inadimplência - podemos descartar essas pessoas para seguir apenas com quem TEVE cartão concedido.
abaixo_16 = bank[bank['IDADE'] < 16]
abaixo_16['CARTAO_CRED'].value_counts()
CARTAO_CRED 1 38 0 16 Name: count, dtype: int64
Decidimos limpar a base para prosseguir, ou seja, retiramos população com menos de 16 anos e também as pessoas que NÃO possuem cartão.
- Passo 01 da Limpeza
bank = bank[~bank.index.isin(abaixo_16.index)]
bank.shape
(13390, 14)
- Passo 02 da Limpeza
## filtrando o dataset e ficando apenas com quem teve cartão concedido
bank = bank[bank['CARTAO_CRED']==1]
Apesar de serem poucas colunas, vamos seguir os estudos com Feature Selection
03 - Separação dos Dados¶
## bibliotecas necessárias
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from category_encoders import TargetEncoder, OneHotEncoder
from sklearn.feature_selection import SelectKBest, f_classif, VarianceThreshold
from sklearn.utils.class_weight import compute_class_weight
from sklearn.metrics import log_loss, roc_auc_score, precision_score, recall_score, f1_score, accuracy_score
from sklearn.linear_model import LogisticRegression
numerical_cols = bank.select_dtypes(exclude='O').columns.tolist()
categorical_cols = bank.select_dtypes(include='object').columns.tolist()
numerical_cols = [col for col in numerical_cols if col not in ['INADIMPLENCIA']]
X = bank.drop(['INADIMPLENCIA'], axis=1)
y = bank['INADIMPLENCIA']
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 42, test_size=0.2)
X_train.shape, X_test.shape, y_train.shape, y_test.shape
((8368, 13), (2093, 13), (8368,), (2093,))
Ponto de Atenção: Sempre tratar os dados APÓS divisão dos dados para evita Data Leakage¶
04 - Pipeline e Treinamento¶
from sklearn.utils.class_weight import compute_class_weight ## 2 - segunda etapa para tentar melhorar o modelo - inserindo pesos
class_weights = compute_class_weight('balanced', classes=np.unique(y_train), y = y_train)
weights_dict = {class_label: weight for class_label, weight in zip(np.unique(y_train), class_weights)}
numeric_transformer = Pipeline(steps = [('imputer', SimpleImputer(strategy='median'))])
categorical_transformer = Pipeline(steps = [('encoder', TargetEncoder())])
preprocessor = ColumnTransformer(
transformers = [
('num', numeric_transformer, numerical_cols),
('cat', categorical_transformer, categorical_cols)
]
)
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('variance_threshold', VarianceThreshold()), # Adicionado para remover features constantes
('select', SelectKBest(f_classif, k=10)), ## 1 - após rodar inicialmente "sem" - inserindo select KBest para tentar melhorar o modelo
('classifier', LogisticRegression(class_weight=weights_dict, max_iter=1000))
])
pipeline.fit(X_train, y_train)
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median'))]),
['CARTAO_CRED', 'IDADE',
'TEMPO_RESIDENCIA',
'DEPENDENTES',
'DEPREC_MAJOR',
'DEPREC_MINOR',
'CASA_PROPRIA',
'RENDA_MENSAL', 'AUTONOMO',
'RENDA/DEPENDENTES',
'EXP_INC',
'DESPESA_MENSAL_CC',
'REGISTRO_GASTOS']),
('cat',
Pipeline(steps=[('encoder',
TargetEncoder())]),
[])])),
('variance_threshold', VarianceThreshold()),
('select', SelectKBest()),
('classifier',
LogisticRegression(class_weight={0: 0.5520517218630426,
1: 5.302915082382763},
max_iter=1000))])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median'))]),
['CARTAO_CRED', 'IDADE',
'TEMPO_RESIDENCIA',
'DEPENDENTES',
'DEPREC_MAJOR',
'DEPREC_MINOR',
'CASA_PROPRIA',
'RENDA_MENSAL', 'AUTONOMO',
'RENDA/DEPENDENTES',
'EXP_INC',
'DESPESA_MENSAL_CC',
'REGISTRO_GASTOS']),
('cat',
Pipeline(steps=[('encoder',
TargetEncoder())]),
[])])),
('variance_threshold', VarianceThreshold()),
('select', SelectKBest()),
('classifier',
LogisticRegression(class_weight={0: 0.5520517218630426,
1: 5.302915082382763},
max_iter=1000))])ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median'))]),
['CARTAO_CRED', 'IDADE', 'TEMPO_RESIDENCIA',
'DEPENDENTES', 'DEPREC_MAJOR', 'DEPREC_MINOR',
'CASA_PROPRIA', 'RENDA_MENSAL', 'AUTONOMO',
'RENDA/DEPENDENTES', 'EXP_INC',
'DESPESA_MENSAL_CC', 'REGISTRO_GASTOS']),
('cat',
Pipeline(steps=[('encoder', TargetEncoder())]),
[])])['CARTAO_CRED', 'IDADE', 'TEMPO_RESIDENCIA', 'DEPENDENTES', 'DEPREC_MAJOR', 'DEPREC_MINOR', 'CASA_PROPRIA', 'RENDA_MENSAL', 'AUTONOMO', 'RENDA/DEPENDENTES', 'EXP_INC', 'DESPESA_MENSAL_CC', 'REGISTRO_GASTOS']
SimpleImputer(strategy='median')
[]
TargetEncoder()
VarianceThreshold()
SelectKBest()
LogisticRegression(class_weight={0: 0.5520517218630426, 1: 5.302915082382763},
max_iter=1000)from sklearn.preprocessing import FunctionTransformer
from sklearn.model_selection import cross_val_score
# Defina a lista de valores de k que você deseja testar
valores_k = [3, 6, 10, 12]
# Crie listas vazias para armazenar os resultados
resultados = []
for k in valores_k:
# Crie o pipeline
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('variance_threshold', VarianceThreshold()), # Remover features constantes
('select', SelectKBest(f_classif, k=k)),
('classifier', LogisticRegression(class_weight=weights_dict, max_iter=1000))
])
# Treine o pipeline
pipeline.fit(X_train, y_train)
# Avalie o pipeline usando cross-validation
scores = cross_val_score(pipeline, X_train, y_train, cv=5) # Use 5-fold cross-validation
mean_score = np.mean(scores)
# Armazene os resultados
resultados.append((k, mean_score))
# Exiba os resultados
for k, score in resultados:
print(f"Para k={k}, a média dos scores é: {score}")
# Encontre o melhor valor de k baseado nos resultados
melhor_k, melhor_score = max(resultados, key=lambda x: x[1])
print(f"Melhor valor de k encontrado: {melhor_k} com score médio de: {melhor_score}")
Para k=3, a média dos scores é: 0.5702618936928562 Para k=6, a média dos scores é: 0.6362374232397178 Para k=10, a média dos scores é: 0.6314589506113328 Para k=12, a média dos scores é: 0.5896428696401703 Melhor valor de k encontrado: 6 com score médio de: 0.6362374232397178
Conseguimos Encontrar o Melhor Valor De K¶
from sklearn.pipeline import Pipeline
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
import numpy as np
# Defina o melhor valor de k encontrado
melhor_k = 6
# Crie o pipeline usando o melhor valor de k
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('variance_threshold', VarianceThreshold()), # Remover features constantes
('select', SelectKBest(f_classif, k=melhor_k)),
('classifier', LogisticRegression(class_weight=weights_dict, max_iter=1000))
])
# Treine o pipeline
pipeline.fit(X_train, y_train)
# Obtenha as predições usando validação cruzada
predicoes = cross_val_predict(pipeline, X_train, y_train, cv=5)
# Calcule as métricas
acuracia = accuracy_score(y_train, predicoes)
precisao = precision_score(y_train, predicoes)
recall = recall_score(y_train, predicoes)
f1 = f1_score(y_train, predicoes)
# Exiba os resultados
print(f"Acurácia: {acuracia}")
print(f"Precisão: {precisao}")
print(f"Recall: {recall}")
print(f"F1-Score: {f1}")
Acurácia: 0.6362332695984704 Precisão: 0.12024250589424049 Recall: 0.4524714828897338 F1-Score: 0.18999467802022352
Após Visualizar resultados Não Tão Bons - Vamos Lidar Agora Com Dados Desbalanceados¶
Cross-Validation Avançada: Usando StratifiedKFold para manter a proporção das classes em cada fold.¶
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
# Defina o novo classificador
classificador = RandomForestClassifier(class_weight=weights_dict, n_estimators=100, random_state=42)
# Crie o pipeline usando o melhor valor de k
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('variance_threshold', VarianceThreshold()), # Remover features constantes
('select', SelectKBest(f_classif, k=melhor_k)),
('classifier', classificador)
])
# Avaliação com validação cruzada estratificada
skf = StratifiedKFold(n_splits=5)
scores = {'accuracy': [], 'precision': [], 'recall': [], 'f1': []}
for train_index, test_index in skf.split(X_train, y_train):
X_fold_train, X_fold_test = X_train.iloc[train_index], X_train.iloc[test_index]
y_fold_train, y_fold_test = y_train.iloc[train_index], y_train.iloc[test_index]
pipeline.fit(X_fold_train, y_fold_train)
predicoes = pipeline.predict(X_fold_test)
scores['accuracy'].append(accuracy_score(y_fold_test, predicoes))
scores['precision'].append(precision_score(y_fold_test, predicoes))
scores['recall'].append(recall_score(y_fold_test, predicoes))
scores['f1'].append(f1_score(y_fold_test, predicoes))
# Exiba os resultados médios
print(f"Acurácia média: {np.mean(scores['accuracy'])}")
print(f"Precisão média: {np.mean(scores['precision'])}")
print(f"Recall médio: {np.mean(scores['recall'])}")
print(f"F1-Score médio: {np.mean(scores['f1'])}")
Acurácia média: 0.903680637234423 Precisão média: 0.21666666666666665 Recall médio: 0.007602999274369105 F1-Score médio: 0.01465056083367726
Aplicando SMOTE Como Forma de Lidar Com Dados Desbalanceados¶
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
# Aplicar SMOTE para balancear as classes
smote = SMOTE(random_state=42)
X_train_res, y_train_res = smote.fit_resample(X_train, y_train)
# Novo classificador
classificador = RandomForestClassifier(class_weight=weights_dict, n_estimators=100, random_state=42)
# Pipeline com melhor k
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('variance_threshold', VarianceThreshold()), # Remover features constantes
('select', SelectKBest(f_classif, k=melhor_k)),
('classifier', classificador)
])
# Avaliação com validação cruzada estratificada
skf = StratifiedKFold(n_splits=5)
scores = {'accuracy': [], 'precision': [], 'recall': [], 'f1': []}
for train_index, test_index in skf.split(X_train_res, y_train_res):
X_fold_train, X_fold_test = X_train_res.iloc[train_index], X_train_res.iloc[test_index]
y_fold_train, y_fold_test = y_train_res.iloc[train_index], y_train_res.iloc[test_index]
pipeline.fit(X_fold_train, y_fold_train)
predicoes = pipeline.predict(X_fold_test)
scores['accuracy'].append(accuracy_score(y_fold_test, predicoes))
scores['precision'].append(precision_score(y_fold_test, predicoes))
scores['recall'].append(recall_score(y_fold_test, predicoes))
scores['f1'].append(f1_score(y_fold_test, predicoes))
# Exibir os resultados médios
print(f"Acurácia média: {np.mean(scores['accuracy'])}")
print(f"Precisão média: {np.mean(scores['precision'])}")
print(f"Recall médio: {np.mean(scores['recall'])}")
print(f"F1-Score médio: {np.mean(scores['f1'])}")
Acurácia média: 0.817260711434787 Precisão média: 0.8178422863976882 Recall médio: 0.8146270801222603 F1-Score médio: 0.814516585287274